package cn.melonlib.cores.jpa.comp.dao.db;

import cn.melonlib.cores.commons.model.entity.IEntity;
import cn.melonlib.cores.commons.model.entity.StringIdEntity;
import cn.melonlib.cores.jpa.comp.Dao;
import cn.melonlib.cores.jpa.model.bean.collections.JsonMap;
import cn.melonlib.cores.jpa.model.entity.DbColumnUpdateLog;
import cn.melonlib.cores.jpa.model.entity.DbTableUpdateLog;
import cn.melonlib.cores.model.anno.ModelComment;
import com.google.common.base.CaseFormat;
import freemarker.template.Template;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Repository;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.persistence.*;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Repository
@Lazy(false)
public class DatabaseSchemasCreator {

    @Resource
    private DataSource dataSource;

    @PersistenceContext
    private EntityManager entityManager;

    @Resource
    private Dao dap;

    @Resource
    private FreeMarkerConfigurer freeMarkerConfigurer;

    @Resource
    private DatabaseTypeMaps databaseTypeMaps;

    @PostConstruct
    public void init(){
        System.out.println(databaseTypeMaps);
    }

    @Value("${system.logs.db.file.path:/service/logs/db}")
    private String fileStorePath;

    public void createAllEntitiesSchemas() throws Exception {

    }

    public DbTableUpdateLog createEntitySchema(Class<? extends IEntity> entityClass) throws Exception {
        Template sqltpl = freeMarkerConfigurer.getConfiguration().getTemplate(databaseTypeMaps.getTemplate());
        StringWriter writer = new StringWriter();

        List<Field> fields = new ArrayList<>();
        fields.addAll(
                Arrays.stream(entityClass.getDeclaredFields()).filter(f -> AnnotationUtils.findAnnotation(f, Transient.class) == null
                ||AnnotationUtils.findAnnotation(f, Id.class) == null
                ).collect(Collectors.toList())
        );
        Class superClass = entityClass.getSuperclass();
        System.out.println(superClass);
        while (ObjectUtils.isNotEmpty(superClass)&&!superClass.equals(StringIdEntity.class)) {
            fields.addAll(
                    Arrays.stream(superClass.getDeclaredFields()).filter(f -> AnnotationUtils.findAnnotation(f, Transient.class) == null).collect(Collectors.toList())
            );
            superClass = superClass.getSuperclass();
            System.out.println(superClass);
        }

        JsonMap typeMaps = JsonMap.createMap();
        ModelComment ec = AnnotationUtils.findAnnotation(entityClass, ModelComment.class);
        DbTableUpdateLog dbTableUpdateLog=new DbTableUpdateLog();

        typeMaps.append("name", AnnotationUtils.findAnnotation(entityClass, Table.class).name())
                .append("comment", ec == null ? "暂缺" : ec.value())
                .append("feilds", fields.stream().filter(f -> AnnotationUtils.findAnnotation(f, ModelComment.class) != null)
                        .map(f -> {
                            JsonMap<String> fMaps = JsonMap.createMap();
                            Column column=AnnotationUtils.findAnnotation(f, Column.class);
                            String fieldName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE
                                    , column==null||StringUtils.isBlank(column.name())?f.getName():column.name());
                            if (IEntity.class.isAssignableFrom(f.getType())) {
                                JoinColumn jc = AnnotationUtils.findAnnotation(f, JoinColumn.class);
                                fieldName = jc == null ? fieldName : jc.name();
                            }
                            fMaps.append("name", fieldName)
                                    .append("type", databaseTypeMaps.map(f)).append("comment", AnnotationUtils.findAnnotation(f, ModelComment.class).value());
                            return fMaps;
                        }).collect(Collectors.toList()));
        dbTableUpdateLog.setName(typeMaps.get("name").toString());
        dbTableUpdateLog.setComment(typeMaps.get("comment").toString());
        sqltpl.process(JsonMap.createMap().append("tables", Arrays.asList(typeMaps)), writer);
        System.out.println(writer);
        Statement stat = this.dataSource.getConnection().createStatement();
        List<DbColumnUpdateLog> dbColumnUpdateLogs=Arrays.stream(writer.toString().split(";")).filter(StringUtils::isNotBlank).map(sql -> {
            DbColumnUpdateLog dbCreatorLog=new DbColumnUpdateLog();
            dbCreatorLog.setSql(sql);
            dbCreatorLog.setStartTime(System.currentTimeMillis());
            try {
                stat.addBatch(sql);
                String path = fileStorePath + "/" + DateFormatUtils.format(System.currentTimeMillis(), "yyyy/MM/dd") + "/db.log";
                dbCreatorLog.setResult("success");
            } catch (SQLException throwables) {
                StringWriter sw=new StringWriter();
                throwables.printStackTrace(new PrintWriter(sw));
                dbCreatorLog.setResult(sw.toString());
            }finally {
                dbCreatorLog.setEndTime(System.currentTimeMillis());
                dbCreatorLog.setUsedTime(dbCreatorLog.getEndTime()- dbCreatorLog.getStartTime());
            }
            return dbCreatorLog;
        }).toList();
        dbTableUpdateLog.setColumnUpdateLogs(dbColumnUpdateLogs);
        try {
            stat.executeBatch();
            dbTableUpdateLog.setResult("success");
        }catch (Exception e){
            StringWriter writer1=new StringWriter();
            e.printStackTrace(new PrintWriter(writer1));
            dbTableUpdateLog.setResult(writer1.toString());
        }
        return dbTableUpdateLog;
    }
}
